﻿#ifndef __RPC_ROUTER_HEAD__
#define __RPC_ROUTER_HEAD__

#include "singleton.h"  // for Singleton

#include <cstdint>  // for uint16_t
#include <string>
#include <unordered_map>

// Rcp路由表。根据服务名和方法名查找目的服ID.
// 不同客户端有不同的路由表.
class CRpcRouter : public Singleton<CRpcRouter>
{
public:
	CRpcRouter();
	virtual ~CRpcRouter();

public:
	using string = std::string;

public:
	// return 0 if no route.
	uint16_t GetDstSvrId(uint64_t uRpcCltId,
		const string& sService, const string& sMethod) const;

	// 设置特定方法的路由，服务的路由不变
	void SetMthdDstSvrId(const string& sService,
		const string& sMethod, uint16_t uSvrId);
	void SetMthdDstSvrId(uint64_t uRpcCltId, const string& sService,
		const string& sMethod, uint16_t uSvrId);
	// 设置服务的路由，特定方法的路由不变
	void SetSvcDstSvrId(const string& sService, uint16_t uSvrId);
	void SetSvcDstSvrId(uint64_t uRpcCltId,
		const string& sService, uint16_t uSvrId);

	// Todo: 断开时删除客户路由。

private:
	struct Router;
	const Router* GetCltRouter(uint64_t uRpcCltId) const;

	// return 0 if no route.
	uint16_t GetDstSvrId(const Router& router,
		const string& sService, const string& sMethod) const;

private:
	using Str2Id = std::unordered_map<string, uint16_t>;
	uint16_t GetIdFromMap(const Str2Id& str2Id, const string& s) const;

private:
	// 多数服务是全部转发，个别方法可能会转发到另一个服。
	// 如副本配对请求会发到配对服务器，而其他请求会发到副本服。
	using Svc2SvrId = Str2Id;  // 服务->SvrId
	using Mthd2SvrId = Str2Id;  // (服务->)方法->SvrId
	// 服务->方法->SvrId
	using Svc2Mthds = std::unordered_map<string, Mthd2SvrId>;

private:
	// return 0 if no route.
	uint16_t GetIdBySvcMthd(const Svc2Mthds& svc2Mthds,
		const string& sService, const string& sMethod) const;
	uint16_t GetIdBySvc(const Svc2SvrId& svc2SvrId,
		const string& sService) const;

private:
	struct Router
	{
		Svc2SvrId svc2SvrId;  // 服务转发
		Svc2Mthds svc2Mthds;  // 服务区分方法名再转发
	};

private:
	Router m_globalRouter;  // 所有客户端都相同的路由

	// 按客户端不同而路由不同, uRpcCltId -> Router
	using RpcCltId2Router = std::unordered_map<uint64_t, Router>;
	RpcCltId2Router m_rpcCltId2Router;
};  // class CRpcRouter

#endif  // __RPC_ROUTER_HEAD__
